#!/usr/bin/env python
import math
import datetime
import datetools
import os.path
import finpy.data
from pysqlite2 import dbapi2 as sqlite

__author__ = 'Ramesh Balasubramanian <ramesh@finpy.org>'
__version__ = "$Revision: 1.14 $"
__credits__ = 'Interface defintion and comments based on MATLAB functions in financial toolbox'

"""
TODO:
1. yielddata and yieldcurve -- if data is not available for requested date, show data for closest
date for which data is available, not most recent
2. support multiple dates and things like Q1 -- and ability to average ...
3. diagram should be refreshed -- for each call ...
"""

def beytbill(settlement, maturity, discountRate):
	"""
	-------------------------------------------------------------------------------
	Usage
		settlement
			Enter as finpy.datenum or dateString. 
			settlementDate must be earlier than or equal to maturityDate.
		maturity
			Enter as finpy.datenum or dateString.
		discountRate
			Discount rate of the Treasury bill. Enter as decimal fraction.
	
	Notes
		Bond equivalent yield for Treasury bill
	
	Examples
		In [1]: import finpy

		In [2]: rate = finpy.beytbill('11-Feb-2000', '8/7/00', 0.0577)

		In [3]: print rate
		0.0602
	-------------------------------------------------------------------------------
	"""
	assert discountRate >= 0 and discountRate <= 1, "discountRate must be >= 0 and <= 1"

	settlementDate = datetools.datenum(settlement)
	maturityDate = datetools.datenum(maturity)
	ndays = maturityDate - settlementDate

	assert ndays >= 0, "settlementDate must be earlier than or equal to maturityDate"

	discountAmount = (1.0 * ndays/360) * 100 * discountRate
	price = 100 - discountAmount
	yd = 100/price
	t = (365 * 1.0 /ndays)
	return round((math.sqrt(yd ** t) - 1) * 2, 4)


def yielddata(recordDate = datetools.today()):
	"""
	Usage
		recent = yielddata()
		data   = yielddata(date)
		
	Notes
		returns treasury yield data for a given date. date defaults to today or the most
		recent date for which yield information is available.
		
		returned dictionary has the different maturity (in years) as key and the yield rate
		(in percentage) as value.
		
	Examples
	
	"""	
	sql = """select * from yield_curve where record_date = %d""" \
			% (recordDate)
	row = __fetchdata(sql)
	if row == None:
		sql = """select * 
				 from yield_curve 
				 where record_date = (select max(record_date) from yield_curve)"""
		row = __fetchdata(sql)
		
	xaxis = [1.0/12, 3.0/12, 6.0/12, 1, 2, 3, 5, 7, 10, 20, 30]
	yaxis = list(row[1:])
	periodRate = filter(lambda pair: pair[1] != '', zip(xaxis,yaxis))
	x = [pair[0] for pair in periodRate]
	y = [pair[1] for pair in periodRate]
	return datetools.datestr(row[0]+366), dict(iter(zip(x,y)))


def yieldcurve(recordDate = datetools.today(), file = None):
	"""
	Usage
	
	Notes
	
	Examples
	
	"""
	import pylab
	date, data = yielddata(recordDate)
	x = data.keys()
	x.sort()
	y = [data[i] for i in x]
	limits = [0, max(x)+2, 0, max(y)*1.5]
	pylab.xlabel('Maturity (years)')
	pylab.ylabel('Yield (percentage)')
	pylab.title('Yield Curve ' + date)
	assert y != None
	assert len(x) == len(y)
	pylab.plot(x,y)
	pylab.axis(limits)
	pylab.show()
	if file != None:
		pylab.savefig(file, format='png', dpi=60)


def acrubond(issueDate, settlementDate, firstCouponDate, faceValue, couponRate, period = 2, basis = 0):
	"""
	-------------------------------------------------------------------------------
	Description
		Accrued interest of security with periodic interest payments
	Parameters
		issueDate 		Enter as serial date number.
		settlementDate	Enter as serial date number.
		firstCouponDate	Enter as serial date number.
		face			Redemption (par, face) value.
		couponRate		Enter as decimal fraction.
		(period)		Coupons per year of the bond.
						Allowed values are 1, 2 (default), 3, 4, 6, and 12.
		(basis)			Day-count basis of the instrument. A vector of integers. 
						0 = actual/actual (default)
						1 = 30/360 (SIA)
						2 = actual/360
						3 = actual/365
						4 = 30/360 (PSA)
						5 = 30/360 (ISDA)
						6 = 30/360 (European)
						7 = actual/365 (Japanese)
	Not Implemented
		basis
	-------------------------------------------------------------------------------
	"""
	assert period in [1, 2, 3, 4, 6, 12]
	if settlementDate > firstCouponDate:
		# bug -- should really find the nearest coupon payments before and after the
		# settlement date and go off of that.
		# best to implement the cashflows and use that method internally for implementing this.
		nextCouponDate = addmonths(firstCouponDate, 12/period)
		lastCouponDate = firstCouponDate
	else:
		nextCouponDate = firstCouponDate
		lastCouponDate = issueDate
	return \
		faceValue*couponRate*\
			(1.0*(settlementDate-lastCouponDate)/(nextCouponDate-lastCouponDate))/period


def __fetchdata(sql):
	dataDirPath = os.path.split(finpy.data.__file__)[0]
	dataFilePath = os.path.join(dataDirPath, 'treasury.db')
	connection = sqlite.connect(dataFilePath)
	cursor = connection.cursor()
	cursor.execute(sql)
	row = cursor.fetchone()
	cursor.close()
	connection.close()
	return row


if __name__ == '__main__':
	pass
